#include "sockbase.h"

#include <errno.h> //errno
#include <poll.h>
#include <string.h>
#include <sys/types.h>

SockBase::SockBase() : m_fd(-1), m_err("")
{
}

SockBase::~SockBase()
{
}

bool SockBase::TimeoutRWCheck(int fd, int timeout, bool bRead)
{
    struct pollfd pollfd;

    pollfd.fd = fd;
    if (bRead) {
        pollfd.events = POLLIN;
    } else {
        pollfd.events = POLLOUT;
    }

    errno = 0;
    for (;;)
    {
        switch (poll(&pollfd, 1, timeout)) {
            case -1:
                if (errno != EINTR) {
                    return false;
                }
                continue;
            case 0:
                errno = ETIMEDOUT;
                return false;
            default:
                if (pollfd.revents & POLLNVAL) {
                    return false;
                }
                return true;
        }
    }
    return true;
}

int SockBase::Read(void *data, int size, int timeout)
{
    if (timeout < 0) {
        m_err = "time out value error";
        return -1; // 超时时间设置错误
    } else if (timeout > 0) {
        if (false == TimeoutRWCheck(m_fd, timeout, true) || errno < 0) {
            m_err = "TimeoutRWCheck error";
            return -2; // 超时或发生错误
        }
    }
    char *ptr = (char *)data;
    int nLeft = size;
    while (nLeft > 0) {
        int nRecv = -1;
        if ((nRecv = read(m_fd, ptr, nLeft)) < 0) {
            if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
                nRecv = 0;
            } else {
                m_err = strerror(errno);
                return -3;
            }
        } else if (nRecv == 0) {
            break;
        }
        nLeft -= nRecv;
        ptr += nRecv;
    }
    return (size - nLeft);
}

int SockBase::ReadLine(void *data, int max_len, int timeout)
{
    if (timeout < 0) {
        m_err = "time out value error";
        return -1; // 超时时间设置错误
    }
    if (false == TimeoutRWCheck(fd, timeout, 1) ) {
        m_err = "time out";
        return -2; // 超时或发生错误
    }
    int line_len = 0;
    int line_flag = 0;
    int check_len = 512;
    do {
        char *ptr = (char *)malloc(check_len);
        if (NULL == ptr) {
            m_err = "malloc failed";
            return -3;
        } else {
            memset(ptr, '\0', check_len);
            if (recv(fd, ptr, check_len - 1, MSG_PEEK) <= 0) {
                free(ptr);
                m_err = "recv failed";
                return -4;
            }
            char *tmp = ptr + line_len;
            while ('\0' != *tmp) {
                line_len++;
                if (*tmp == '\n' || line_len >= max_len) { //读取到换行或最大读取字节数
                    line_flag = 1;
                    break;
                }
                tmp++;
            }
            if (0 == line_flag) {
                check_len += 512;
            }
            free(ptr);
            ptr = NULL;
        }
    } while (line_flag == 0);

    return Read(fd, data, line_len, timeout);
}

int SockBase::Write(void *data, int size, int timeout)
{
    if (timeout < 0) {
        m_err = "time out value error";
        return -1; // 超时时间设置错误
    } else if (timeout > 0) {
        if (false == TimeoutRWCheck(m_fd, timeout, false) || errno < 0) {
            return -2; // 超时或发生错误
        }
    }
    char *ptr = (char *)data;
    int nLeft = 0;
    int nSend = 0;
    nLeft = size;
    while (nLeft > 0) {
        if ((nSend = write(m_fd, ptr, nLeft)) <= 0) {
            if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
                nSend = 0;
            else {
                m_err = strerror(errno);
                return -3;
            }
        }
        nLeft -= nSend;
        ptr += nSend;
    }
    return (size - nLeft);
}

const char *SockBase::GetErrorInfo()
{
    return m_err.c_str();
}

int SockBase::Close()
{
    int ret = 0;
    if (m_fd > 0) {
        ret = close(m_fd);
        if (ret != 0) {
            m_err += strerror(errno);
        }
        m_fd = -1;
    }
    return ret;
}
